// configurable hotkey module, added by regret
// based on FBA-RR (http://code.google.com/p/fbarr/)

/* changelog:
 update 6: add update menu accel string
 update 5: partial rewrite
 update 4: add key up handle and multiple modifiers support
 update 3: code cleanup
 update 2: add key string for translation
 update 1: create (ref: fba-rr)
*/

#include "burner.h"
#include "maphkeys.h"
#include "tracklst.h"
#include "cheat.h"

enum { MODKEY_NONE = 0x00, MODKEY_CTRL = 0x01, MODKEY_ALT = 0x02, MODKEY_SHIFT = 0x04 };

// init keys
CustomKey customKeys[] = {
	{ VK_PAUSE,      MODKEY_NONE,  0,                      IDS_HOTKEY_PAUSE,        "pause",         HK_pause,           0, 0 },
	{ VK_SPACE,      MODKEY_NONE,  0,                      IDS_HOTKEY_FASTFOWARD,   "fast-foward",   HK_fastFowardKeyDown, HK_fastFowardKeyUp, 0 },
	{ VK_OEM_5,      MODKEY_NONE,  0,                      IDS_HOTKEY_FRAMEADVANCE, "frame-advance", HK_frameAdvance,    0, 0 },
	{ '9',           MODKEY_SHIFT, 0,                      IDS_HOTKEY_READONLY,     "readonly",      HK_toggleReadOnly,  0, 0 },
	{ VK_OEM_MINUS,  MODKEY_SHIFT, 0,                      IDS_HOTKEY_DECSPEED,     "dec-speed",     HK_speedDec,        0, 0 },
	{ VK_OEM_PLUS,   MODKEY_SHIFT, 0,                      IDS_HOTKEY_INCSPEED,     "inc-speed",     HK_speedInc,        0, 0 },
	{ VK_OEM_PERIOD, MODKEY_NONE,  0,                      IDS_HOTKEY_FRAMECOUNTER, "frame-counter", HK_frameCounter,    0, 0 },
	{ VK_F12,        MODKEY_NONE,  MENU_SAVESNAP,          IDS_HOTKEY_SCREENSHOT,   "screenshot",    HK_screenShot,      0, 0 },

	{ 0,             MODKEY_NONE,  0,                      IDS_HOTKEY_LOADSTATE1,   "loadstate1",    HK_loadState,       0, 1 },
	{ 0,             MODKEY_NONE,  0,                      IDS_HOTKEY_LOADSTATE2,   "loadstate2",    HK_loadState,       0, 2 },
	{ 0,             MODKEY_NONE,  0,                      IDS_HOTKEY_LOADSTATE3,   "loadstate3",    HK_loadState,       0, 3 },
	{ 0,             MODKEY_NONE,  0,                      IDS_HOTKEY_LOADSTATE4,   "loadstate4",    HK_loadState,       0, 4 },
	{ 0,             MODKEY_NONE,  0,                      IDS_HOTKEY_LOADSTATE5,   "loadstate5",    HK_loadState,       0, 5 },
	{ 0,             MODKEY_NONE,  0,                      IDS_HOTKEY_LOADSTATE6,   "loadstate6",    HK_loadState,       0, 6 },
	{ 0,             MODKEY_NONE,  0,                      IDS_HOTKEY_LOADSTATE7,   "loadstate7",    HK_loadState,       0, 7 },
	{ 0,             MODKEY_NONE,  0,                      IDS_HOTKEY_LOADSTATE8,   "loadstate8",    HK_loadState,       0, 8 },
	{ 0,             MODKEY_NONE,  0,                      IDS_HOTKEY_LOADSTATE9,   "loadstate9",    HK_loadState,       0, 9 },
	{ 0,             MODKEY_NONE,  0,                      IDS_HOTKEY_SAVESTATE1,   "savestate1",    HK_saveState,       0, 1 },
	{ 0,             MODKEY_NONE,  0,                      IDS_HOTKEY_SAVESTATE2,   "savestate2",    HK_saveState,       0, 2 },
	{ 0,             MODKEY_NONE,  0,                      IDS_HOTKEY_SAVESTATE3,   "savestate3",    HK_saveState,       0, 3 },
	{ 0,             MODKEY_NONE,  0,                      IDS_HOTKEY_SAVESTATE4,   "savestate4",    HK_saveState,       0, 4 },
	{ 0,             MODKEY_NONE,  0,                      IDS_HOTKEY_SAVESTATE5,   "savestate5",    HK_saveState,       0, 5 },
	{ 0,             MODKEY_NONE,  0,                      IDS_HOTKEY_SAVESTATE6,   "savestate6",    HK_saveState,       0, 6 },
	{ 0,             MODKEY_NONE,  0,                      IDS_HOTKEY_SAVESTATE7,   "savestate7",    HK_saveState,       0, 7 },
	{ 0,             MODKEY_NONE,  0,                      IDS_HOTKEY_SAVESTATE8,   "savestate8",    HK_saveState,       0, 8 },
	{ 0,             MODKEY_NONE,  0,                      IDS_HOTKEY_SAVESTATE9,   "savestate9",    HK_saveState,       0, 9 },
	{ VK_F8,         MODKEY_NONE,  MENU_STATE_PREVSLOT,    IDS_HOTKEY_SELPREVSTAT,  "sel-prevstate", HK_prevState,       0, 0 },
	{ VK_F11,        MODKEY_NONE,  MENU_STATE_NEXTSLOT,    IDS_HOTKEY_SELNEXTSTAT,  "sel-nextstate", HK_nextState,       0, 0 },
	{ VK_F9,         MODKEY_NONE,  MENU_STATE_LOAD_SLOT,   IDS_HOTKEY_LOADCURSTAT,  "load-curstate", HK_loadCurState,    0, 0 },
	{ VK_F10,        MODKEY_NONE,  MENU_STATE_SAVE_SLOT,   IDS_HOTKEY_SAVECURSTAT,  "save-curstate", HK_saveCurState,    0, 0 },
	{ VK_F9,         MODKEY_CTRL,  MENU_STATE_LOAD_DIALOG, IDS_HOTKEY_LOADSTATDIAG, "load-dialog",   HK_loadStateDialog, 0, 0 },
	{ VK_F10,        MODKEY_CTRL,  MENU_STATE_SAVE_DIALOG, IDS_HOTKEY_SAVESTATDIAG, "save-dialog",   HK_saveStateDialog, 0, 0 },

	{ 'R',           MODKEY_ALT,   MENU_STARTRECORD,       IDS_HOTKEY_STARTRECORD,  "start-rec",     HK_startRec,        0, 0 },
	{ 'P',           MODKEY_ALT,   MENU_STARTREPLAY,       IDS_HOTKEY_PLAYRECORD,   "play-rec",      HK_playRec,         0, 0 },
	{ 'T',           MODKEY_ALT,   MENU_STOPREPLAY,        IDS_HOTKEY_ENDRECORD,    "end-rec",       HK_stopRec,         0, 0 },
	{ 'V',           MODKEY_ALT,   MENU_AVISTART,          IDS_HOTKEY_STARTAVI,     "start-avi",     HK_startAvi,        0, 0 },
	{ 'E',           MODKEY_ALT,   MENU_AVISTOP,           IDS_HOTKEY_ENDAVI,       "end-avi",       HK_stopAvi,         0, 0 },
	{ VK_F1,         MODKEY_CTRL,  MENU_ENABLECHEAT,       IDS_HOTKEY_CHEATEDIT,    "cheat",         HK_cheatEditor,     0, 0 },
	{ VK_OEM_MINUS,  MODKEY_NONE,  0,                      IDS_HOTKEY_VOLUMEDOWN,   "volume-down",   HK_volumeDec,       0, 0 },
	{ VK_OEM_PLUS,   MODKEY_NONE,  0,                      IDS_HOTKEY_VOLUMEUP,     "volume-up",     HK_volumeInc,       0, 0 },
	{ VK_BACK,       MODKEY_NONE,  0,                      IDS_HOTKEY_SHOWFPS,      "show-fps",      HK_showFps,         0, 0 },
	{ VK_F6,         MODKEY_NONE,  MENU_LOAD,              IDS_HOTKEY_OPENGAME,     "open-game",     HK_openGame,        0, 0 },
	{ VK_F6,         MODKEY_SHIFT, MENU_LOAD_QUICK,        IDS_HOTKEY_QUICKOPEN,    "quick-open",    HK_quickOpenGame,   0, 0 },
	{ VK_F4,         MODKEY_CTRL,  MENU_QUIT,              IDS_HOTKEY_EXITGAME,     "exit-game",     HK_exitGame,        0, 0 },
	{ VK_F5,         MODKEY_NONE,  MENU_INPUT,             IDS_HOTKEY_CONFIGPAD,    "config-pad",    HK_configPad,       0, 0 },
	{ VK_F5,         MODKEY_CTRL,  MENU_DIPSW,             IDS_HOTKEY_DIPSW,        "dips",          HK_setDips,         0, 0 },
	{ VK_F12,        MODKEY_CTRL,  MENU_SNAPFACT,          IDS_HOTKEY_SHOTFACT,     "shot-factory",  HK_shotFactory,     0, 0 },
	{ '1',           MODKEY_ALT,   MENU_WINDOWSIZE1X,      IDS_HOTKEY_WINSIZE1,     "win-size1",     HK_windowSize,      0, 1 },
	{ '2',           MODKEY_ALT,   MENU_WINDOWSIZE2X,      IDS_HOTKEY_WINSIZE2,     "win-size2",     HK_windowSize,      0, 2 },
	{ '3',           MODKEY_ALT,   MENU_WINDOWSIZE3X,      IDS_HOTKEY_WINSIZE3,     "win-size3",     HK_windowSize,      0, 3 },
	{ '4',           MODKEY_ALT,   MENU_WINDOWSIZE4X,      IDS_HOTKEY_WINSIZE4,     "win-size4",     HK_windowSize,      0, 4 },
	{ '5',           MODKEY_ALT,   MENU_WINDOWSIZE5X,      IDS_HOTKEY_WINSIZE5,     "win-size5",     HK_windowSize,      0, 5 },
	{ VK_PRIOR,      MODKEY_ALT,   0,                      IDS_HOTKEY_PREVFILTER,   "prev-filter",   HK_prevFilter,      0, 0 },
	{ VK_NEXT,       MODKEY_ALT,   0,                      IDS_HOTKEY_NEXTFILTER,   "next-filter",   HK_nextFilter,      0, 0 },
	{ VK_RETURN,     MODKEY_ALT,   MENU_FULL,              IDS_HOTKEY_FULLSCREEN,   "fullscreen",    HK_fullscreen,      0, 0 },
	{ 'F',           MODKEY_CTRL,  0,                      IDS_HOTKEY_FAKEFULL,     "fake-full",     HK_fakeFullscreen,  0, 0 },

	{ 0xffff, 0xffff, 0, IDS_ERR_UNKNOWN, "", 0, 0, 0 }, // last key
};

static int nFpsScale = 100;

HWND hMHkeysDlg = NULL;
static HWND hMHkeysList = NULL;

static HHOOK hook = 0;
static int receivingKmap;

const wchar_t* GetKeyName(int c)
{
	static wchar_t out[MAX_PATH];
	swprintf(out, L"#%d", c);

	if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z'))
	{
		swprintf(out, L"%c", c);
		return out;
	}
	if (c >= VK_NUMPAD0 && c <= VK_NUMPAD9)
	{
		swprintf(out, L"Numpad-%c", '0' + (c - VK_NUMPAD0));
		return out;
	}

	switch (c)
	{
		case VK_TAB:        swprintf(out, L"Tab"); break;
		case VK_BACK:       swprintf(out, L"Backspace"); break;
		case VK_CLEAR:      swprintf(out, L"Delete"); break;
		case VK_RETURN:     swprintf(out, L"Enter"); break;
		case VK_LSHIFT:     swprintf(out, L"LShift"); break;
		case VK_RSHIFT:     swprintf(out, L"RShift"); break;
		case VK_LCONTROL:   swprintf(out, L"LCtrl"); break;
		case VK_RCONTROL:   swprintf(out, L"RCtrl"); break;
		case VK_LMENU:      swprintf(out, L"LAlt"); break;
		case VK_RMENU:      swprintf(out, L"RAlt"); break;
		case VK_PAUSE:      swprintf(out, L"Pause"); break;
		case VK_CAPITAL:    swprintf(out, L"CapsLock"); break;
		case VK_ESCAPE:     swprintf(out, L"Escape"); break;
		case VK_SPACE:      swprintf(out, L"Space"); break;
		case VK_PRIOR:      swprintf(out, L"PgUp"); break;
		case VK_NEXT:       swprintf(out, L"PgDn"); break;
		case VK_HOME:       swprintf(out, L"Home"); break;
		case VK_END:        swprintf(out, L"End"); break;
		case VK_LEFT:       swprintf(out, L"Left"); break;
		case VK_RIGHT:      swprintf(out, L"Right"); break;
		case VK_UP:         swprintf(out, L"Up"); break;
		case VK_DOWN:       swprintf(out, L"Down"); break;
		case VK_SELECT:     swprintf(out, L"Select"); break;
		case VK_PRINT:      swprintf(out, L"Print"); break;
		case VK_EXECUTE:    swprintf(out, L"Execute"); break;
		case VK_SNAPSHOT:   swprintf(out, L"SnapShot"); break;
		case VK_INSERT:     swprintf(out, L"Insert"); break;
		case VK_DELETE:     swprintf(out, L"Delete"); break;
		case VK_HELP:       swprintf(out, L"Help"); break;
		case VK_LWIN:       swprintf(out, L"LWin"); break;
		case VK_RWIN:       swprintf(out, L"RWin"); break;
		case VK_APPS:       swprintf(out, L"App"); break;
		case VK_MULTIPLY:   swprintf(out, L"Numpad *"); break;
		case VK_ADD:        swprintf(out, L"Numpad +"); break;
		case VK_SEPARATOR:  swprintf(out, L"\\"); break;
		case VK_OEM_7:      swprintf(out, L"Apostrophe"); break;
		case VK_OEM_COMMA:  swprintf(out, L"Comma");break;
		case VK_OEM_PERIOD: swprintf(out, L"Period");break;
		case VK_SUBTRACT:   swprintf(out, L"Numpad -"); break;
		case VK_DECIMAL:    swprintf(out, L"Numpad ."); break;
		case VK_DIVIDE:     swprintf(out, L"Numpad /"); break;
		case VK_NUMLOCK:    swprintf(out, L"NumLock"); break;
		case VK_SCROLL:     swprintf(out, L"ScrollLock"); break;
		case VK_OEM_MINUS:  swprintf(out, L"-"); break;
		case VK_OEM_PLUS:   swprintf(out, L"="); break;
		case VK_SHIFT:      swprintf(out, L"Shift"); break;
		case VK_CONTROL:    swprintf(out, L"Control"); break;
		case VK_MENU:       swprintf(out, L"Alt"); break;
		case VK_OEM_1:      swprintf(out, L";"); break;
		case VK_OEM_4:      swprintf(out, L"["); break;
		case VK_OEM_6:      swprintf(out, L"]"); break;
		case VK_OEM_5:      swprintf(out, L"\\"); break;
		case VK_OEM_2:      swprintf(out, L"/"); break;
		case VK_OEM_3:      swprintf(out, L"`"); break;
		case VK_F1:         swprintf(out, L"F1"); break;
		case VK_F2:         swprintf(out, L"F2"); break;
		case VK_F3:         swprintf(out, L"F3"); break;
		case VK_F4:         swprintf(out, L"F4"); break;
		case VK_F5:         swprintf(out, L"F5"); break;
		case VK_F6:         swprintf(out, L"F6"); break;
		case VK_F7:         swprintf(out, L"F7"); break;
		case VK_F8:         swprintf(out, L"F8"); break;
		case VK_F9:         swprintf(out, L"F9"); break;
		case VK_F10:        swprintf(out, L"F10"); break;
		case VK_F11:        swprintf(out, L"F11"); break;
		case VK_F12:        swprintf(out, L"F12"); break;
		case VK_F13:        swprintf(out, L"F13"); break;
		case VK_F14:        swprintf(out, L"F14"); break;
		case VK_F15:        swprintf(out, L"F15"); break;
		case VK_F16:        swprintf(out, L"F16"); break;
	}

	return out;
}

// update menu accel string
int MHkeysUpdateMenuAcc()
{
	wchar_t accelStr[MAX_PATH] = L"";

	CustomKey* customkey = &customKeys[0];
	while (!lastCustomKey(*customkey)) {
		if (customkey->menuid > 0) {
			accelStr[0] = '\0';

			if (customkey->keymod & MODKEY_CTRL)
				wcscat(accelStr, L"Ctrl+");
			if (customkey->keymod & MODKEY_ALT)
				wcscat(accelStr, L"Alt+");
			if (customkey->keymod & MODKEY_SHIFT)
				wcscat(accelStr, L"Shift+");

			wcscat(accelStr, GetKeyName(customkey->key));

			menuModifyAccString(customkey->menuid, accelStr);
		}
		customkey++;
	}

	menuSync(MENUT_ALL);

	return 0;
}

// Update which command is using which key
static int MHkeysUseUpdate()
{
	wchar_t tempTxt[MAX_PATH];
	unsigned int i;

	if (hMHkeysList == NULL) {
		return 1;
	}

	// Update the values of all the inputs
	for (i = 0; !lastCustomKey(customKeys[i]); i++) {
		CustomKey& key = customKeys[i];

		LVITEM LvItem;
		tempTxt[0] = '\0';

		if (key.keymod & MODKEY_CTRL)
			wcscat(tempTxt, L"Ctrl + ");
		if (key.keymod & MODKEY_ALT)
			wcscat(tempTxt, L"Alt + ");
		if (key.keymod & MODKEY_SHIFT)
			wcscat(tempTxt, L"Shift + ");

		swprintf(tempTxt, L"%s%s", tempTxt, GetKeyName(key.key));

		if (!key.key)
			tempTxt[0] = '\0';

		memset(&LvItem, 0, sizeof(LvItem));
		LvItem.mask = LVIF_TEXT;
		LvItem.iItem = i;
		LvItem.iSubItem = 1;
		LvItem.pszText = tempTxt;
		ListView_SetItem(hMHkeysList, &LvItem);
	}

	return 0;
}

static int MHkeysListBegin()
{
	LVCOLUMN LvCol;
	if (hMHkeysList == NULL) {
		return 1;
	}

	// Full row select style:
	ListView_SetExtendedListViewStyle(hMHkeysList, LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);

	// Make column headers
	memset(&LvCol, 0, sizeof(LvCol));
	LvCol.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;

	LvCol.cx = 164;
	LvCol.pszText = localiseStringEx(IDS_INPUT_INPUT);
	ListView_InsertColumn(hMHkeysList, 0, &LvCol);

	LvCol.cx = 160;
	LvCol.pszText = localiseStringEx(IDS_INPUT_MAPPING);
	ListView_InsertColumn(hMHkeysList, 1, &LvCol);

	return 0;
}

// Make a list view of the game inputs
int MHkeysListMake(int bBuild)
{
	unsigned int i;

	if (hMHkeysList == NULL) {
		return 1;
	}

	if (bBuild)	{
		ListView_DeleteAllItems(hMHkeysList);
	}

	// Add all the input names to the list
	for (i = 0; !lastCustomKey(customKeys[i]); i++) {
		CustomKey& key = customKeys[i];

		LVITEM LvItem;
		memset(&LvItem, 0, sizeof(LvItem));
		LvItem.mask = LVIF_TEXT | LVIF_PARAM;
		LvItem.iItem = i;
		LvItem.iSubItem = 0;
		LvItem.pszText = localiseStringEx(key.strcode);
		LvItem.lParam = (LPARAM)i;

		if (bBuild) {
			ListView_InsertItem(hMHkeysList, &LvItem);
		} else {
			ListView_SetItem(hMHkeysList, &LvItem);
		}
	}

	MHkeysUseUpdate();

	return 0;
}

static int MHkeysInit()
{
	hMHkeysList = GetDlgItem(hMHkeysDlg, IDC_MHKEYS_LIST);

	MHkeysListBegin();
	MHkeysListMake(1);

	return 0;
}

static int MHkeysExit()
{
	hMHkeysList = NULL;
	hMHkeysDlg = NULL;

	UnhookWindowsHookEx(hook);
	hook = 0;

	MHkeysUpdateMenuAcc();

	return 0;
}

static LRESULT CALLBACK KeyMappingHook(int code, WPARAM wParam, LPARAM lParam)
{
	if (code < 0) {
		return CallNextHookEx(hook, code, wParam, lParam);
	}
	if (wParam == VK_SHIFT || wParam == VK_MENU || wParam == VK_CONTROL) {
		return CallNextHookEx(hook, code, wParam, lParam);
	}

	CustomKey& key = customKeys[receivingKmap];

	key.key = wParam;
	key.keymod = 0;
	if (KEY_DOWN(VK_CONTROL))
		key.keymod |= MODKEY_CTRL;
	if (KEY_DOWN(VK_MENU))
		key.keymod |= MODKEY_ALT;
	if (KEY_DOWN(VK_SHIFT))
		key.keymod |= MODKEY_SHIFT;

	MHkeysUseUpdate();

	UnhookWindowsHookEx(hook);
	hook = 0;

	SetWindowText(GetDlgItem(hMHkeysDlg, IDC_HKEYSS_STATIC), localiseStringEx(IDS_MAPHOTKEY_TIP));
	return 1;
}

// List item(s) deleted; find out which one(s)
static int MHkeysItemDelete()
{
	int start = -1;
	LVITEM LvItem;
	int ret;

	while ((ret = ListView_GetNextItem(hMHkeysList, start, LVNI_SELECTED)) != -1) {
		start = ret;

		// Get the corresponding input
		LvItem.mask = LVIF_PARAM;
		LvItem.iItem = ret;
		LvItem.iSubItem = 0;
		ListView_GetItem(hMHkeysList, &LvItem);
		ret = LvItem.lParam;

		CustomKey& key = customKeys[ret];
		key.key = 0;
		key.keymod = 0;
	}

	MHkeysListMake(0);
	return 0;
}

// List item activated; find out which one
static int MHkeysItemActivate()
{
	receivingKmap = ListView_GetNextItem(hMHkeysList, -1, LVNI_SELECTED);
	if (receivingKmap < 0) {
		return 1;
	}

	CustomKey& key = customKeys[receivingKmap];
	wchar_t keystr[MAX_PATH];
	wcscpy(keystr, localiseStringEx(key.strcode));

	wchar_t str[MAX_PATH];
	swprintf(str, localiseStringEx(IDS_MAPHOTKEY_CLICKTIP), keystr);
	SetWindowText(GetDlgItem(hMHkeysDlg, IDC_HKEYSS_STATIC), str);
	hook = SetWindowsHookEx(WH_KEYBOARD, KeyMappingHook, 0, GetCurrentThreadId());

	return 0;
}

static INT_PTR CALLBACK MHkeysDialogProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
	if (msg == WM_INITDIALOG) {
		hMHkeysDlg = hDlg;
		MHkeysInit();
		wndInMid(hDlg, hScrnWnd);
		return TRUE;
	}

	if (msg == WM_CLOSE) {
		EnableWindow(hScrnWnd, TRUE);
		DestroyWindow(hMHkeysDlg);
		return 0;
	}

	if (msg == WM_DESTROY) {
		MHkeysExit();
		return 0;
	}

	if (msg == WM_COMMAND) {
		int Id = LOWORD(wParam);
		int Notify = HIWORD(wParam);

		if (Id == IDOK && Notify == BN_CLICKED) {
			return 0;
		}
		else if (Id == IDCANCEL && Notify == BN_CLICKED) {
			SendMessage(hDlg, WM_CLOSE, 0, 0);
			return 0;
		}
	}

	if (msg == WM_NOTIFY && lParam != 0) {
		int Id = LOWORD(wParam);
		NMHDR* pnm = (NMHDR*)lParam;

		if (Id == IDC_MHKEYS_LIST && pnm->code == LVN_ITEMACTIVATE) {
			MHkeysItemActivate();
		}
		else if (Id == IDC_MHKEYS_LIST && pnm->code == LVN_KEYDOWN) {
			NMLVKEYDOWN *pnmkd = (NMLVKEYDOWN*)lParam;
			if (pnmkd->wVKey == VK_DELETE) {
				MHkeysItemDelete();
			}
		}

		return 0;
	}

	return 0;
}

int MHkeysCreate(HWND parent)
{
	DestroyWindow(hMHkeysDlg); // Make sure exitted

	localiseDialogBox(IDD_MHKEYS, parent, (DLGPROC)MHkeysDialogProc);
	if (hMHkeysDlg == NULL) {
		return 1;
	}

	ShowWindow(hMHkeysDlg, SW_NORMAL);

	return 0;
}

// ----------------------------------------------------------------------------

// key functions
extern bool useDialogs();
extern void quickLoadFile();

void HK_pause(int)
{
	if (driverOkay && !kNetGame) {
		setPauseMode(!runPause);
	} else {
		setPauseMode(0);
	}
	menuSync(MENUT_FILE);
}

void HK_fastFowardKeyDown(int)
{
	if (!kNetGame && !soundLogStart) {
		appDoFast = 1;
	}
}
void HK_fastFowardKeyUp(int)
{
	appDoFast = 0;
}

void HK_loadState(int param)
{
	statedLoad(param);
}
void HK_saveState(int param)
{
	statedSave(param);
}

void HK_prevState(int)
{
	savestateSlot--;
	if (savestateSlot < 1) {
		savestateSlot = 1;
	}

	wchar_t szString[MAX_PATH];
	_snwprintf(szString, sizearray(szString), localiseStringEx(IDS_STATE_ACTIVESLOT), savestateSlot);
	vidSNewShortMsg(szString);
	menuSync(MENUT_FILE);
}
void HK_nextState(int)
{
	savestateSlot++;
	if (savestateSlot > 8) {
		savestateSlot = 8;
	}

	wchar_t szString[MAX_PATH];
	_snwprintf(szString, sizearray(szString), localiseStringEx(IDS_STATE_ACTIVESLOT), savestateSlot);
	vidSNewShortMsg(szString);
	menuSync(MENUT_FILE);
}

void HK_loadCurState(int)
{
	if (driverOkay && !kNetGame) {
		if (statedLoad(savestateSlot) == 0) {
			if (replayStatus) {
				if (replayReadOnly) {
					vidSNewShortMsg(localiseStringEx(IDS_REP_REWIND));
				} else {
					vidSNewShortMsg(localiseStringEx(IDS_REP_UNDO));
				}
			}
		}
	}
}
void HK_saveCurState(int)
{
	if (driverOkay) {
		statedSave(savestateSlot);
	}
}

void HK_loadStateDialog(int)
{
	if (useDialogs() && !kNetGame) {
		inputInterface.setCooperativeLevel(false, !alwaysProcessKey);
		audioInterface.stop();
		splashDestroy(1);
		statedLoad(0);
		gameInpCheckMouse();
		audioInterface.play();
	}
}
void HK_saveStateDialog(int)
{
	if (useDialogs()) {
		inputInterface.setCooperativeLevel(false, !alwaysProcessKey);
		audioInterface.blank();
		statedSave(0);
		gameInpCheckMouse();
	}
}

void HK_playRec(int)
{
	if (useDialogs()) {
		inputInterface.setCooperativeLevel(false, !alwaysProcessKey);
		audioInterface.stop();
		splashDestroy(1);
		stopReplay();
		startReplay();
		gameInpCheckMouse();
		audioInterface.play();

		menuSync(MENUT_FILE);
	}
}
void HK_startRec(int)
{
	if (useDialogs()) {
		inputInterface.setCooperativeLevel(false, !alwaysProcessKey);
		audioInterface.blank();
		stopReplay();
		startRecord();
		gameInpCheckMouse();

		menuSync(MENUT_FILE);
	}
}
void HK_stopRec(int)
{
	stopReplay();
	menuSync(MENUT_FILE);
}

void HK_startAvi(int)
{
	if (useDialogs()) {
		if (aviStart()) {
			aviStop();
		} else {
			vidSNewShortMsg(localiseStringEx(IDS_REC_AVI), 0x0000FF);
		}
		menuSync(MENUT_FILE);
	}
}
void HK_stopAvi(int)
{
	if (!aviStop()) {
		vidSNewShortMsg(localiseStringEx(IDS_STOP_AVI), 0xFF3F3F);
		menuSync(MENUT_FILE);
	}
}

void HK_frameAdvance(int)
{
	if (!kNetGame && !runPause) {
		runPause = 1;
	}
	appDoStep = 1;
}

void HK_toggleReadOnly(int)
{
	replayReadOnly = !replayReadOnly;
	if (replayReadOnly) {
		vidSNewShortMsg(L"read-only");
	} else {
		vidSNewShortMsg(L"read+write");
	}
}

void HK_frameCounter(int)
{
	frameCounterDisplay = !frameCounterDisplay;
	if (!frameCounterDisplay) {
		vidSKillTinyMsg();
		vidSKillTinyOSDMsg();
	}
}

void HK_speedInc(int)
{
	if (kNetGame) {
		return;
	}

	if (nFpsScale < 10) {
		nFpsScale = 10;
	} else {
		if (nFpsScale >= 100) {
			nFpsScale += 50;
		} else {
			nFpsScale += 10;
		}
	}
	if (nFpsScale > 800) {
		nFpsScale = 800;
	}

	wchar_t buffer[15];
	swprintf(buffer, localiseStringEx(IDS_SPEEDSET), nFpsScale);
	vidSNewShortMsg(buffer);

	mediaChangeFps(nFpsScale);
}
void HK_speedDec(int)
{
	if (kNetGame) {
		return;
	}

	if (nFpsScale <= 10) {
		nFpsScale = 10;
	} else {
		if (nFpsScale > 100) {
			nFpsScale -= 50;
		} else {
			nFpsScale -= 10;
		}
	}
	if (nFpsScale < 10) {
		nFpsScale = 10;
	}

	wchar_t buffer[15];
	swprintf(buffer, localiseStringEx(IDS_SPEEDSET), nFpsScale);
	vidSNewShortMsg(buffer);

	mediaChangeFps(nFpsScale);
}

void HK_volumeDec(int)
{
	audioInterface.audVolume -= 100;
	if (audioInterface.audVolume < 0) {
		audioInterface.audVolume = 0;
	}
	if (audioInterface.setvolume(audioInterface.audVolume) != 0) {
		vidSNewShortMsg(localiseStringEx(IDS_SOUND_NOVOLUME));
	} else {
		wchar_t buffer[15];
		swprintf(buffer, localiseStringEx(IDS_SOUND_VOLUMESET), audioInterface.audVolume / 100);
		vidSNewShortMsg(buffer);
	}
}
void HK_volumeInc(int)
{
	audioInterface.audVolume += 100;
	if (audioInterface.audVolume > 10000) {
		audioInterface.audVolume = 10000;
	}
	if (audioInterface.setvolume(audioInterface.audVolume) != 0) {
		vidSNewShortMsg(localiseStringEx(IDS_SOUND_NOVOLUME));
	} else {
		wchar_t buffer[15];
		swprintf(buffer, localiseStringEx(IDS_SOUND_VOLUMESET), audioInterface.audVolume / 100);
		vidSNewShortMsg(buffer);
	}
}

void HK_showFps(int)
{
	showFPS = !showFPS;
	if (!showFPS) {
		vidSKillShortMsg();
		vidSKillOSDMsg();
		scrnTitle();
	}
}

void HK_configPad(int)
{
	if (useDialogs()) {
		audioInterface.blank();
		inputInterface.setCooperativeLevel(false, false);
		inpdCreate();
	}
}

void HK_setDips(int)
{
	if (useDialogs()) {
		audioInterface.blank();
		inputInterface.setCooperativeLevel(false, !alwaysProcessKey);
		inpDIPSWCreate();
	}
}

void HK_cheatEditor(int)
{
	if (useDialogs()) {
		audioInterface.blank();
		inputInterface.setCooperativeLevel(false, !alwaysProcessKey);
		inpCheatCreate();
	}
}

void HK_windowSize(int param)
{
	extern bool fakeFullscreen;
	if (scrnWindowSize != param && !fakeFullscreen) {
		scrnWindowSize = param;
		simpleReinitScrn(false);
		menuSync(MENUT_SETTING);
	}
}

void HK_fullscreen(int)
{
	if (driverOkay || vidFullscreen) {
		vidFullscreen = !vidFullscreen;
		scrnSwitchFull();
	}
}

void HK_fakeFullscreen(int)
{
	scrnFakeFullscreen();
}

void HK_screenShot(int)
{
	if (!driverOkay) {
		return;
	}

	int status = makeScreenShot();
	if (!status) {
		vidSNewShortMsg(localiseStringEx(IDS_SSHOT_SAVED));
	} else {
		wchar_t tmpmsg[MAX_PATH];
		_snwprintf(tmpmsg, sizearray(tmpmsg), localiseStringEx(IDS_SSHOT_ERROR), status);
		vidSNewShortMsg(tmpmsg, 0xFF3F3F);
	}
}
void HK_shotFactory(int)
{
	if (useDialogs()) {
		audioInterface.blank();
		inputInterface.setCooperativeLevel(false, !alwaysProcessKey);
		shotFactoryCreate();
	}
}

void HK_prevFilter(int)
{
	scrnSize();
	filter.enable = true;
	filter.select(filter.filterId - 1);
	menuSync(MENUT_SETTING);
}
void HK_nextFilter(int)
{
	scrnSize();
	filter.enable = true;
	filter.select(filter.filterId + 1);
	menuSync(MENUT_SETTING);
}

void HK_openGame(int)
{
	if (!useDialogs()) {
		return;
	}

	selDialog(hScrnWnd);		// Bring up select dialog to pick a driver
}
void HK_quickOpenGame(int)
{
	if (!kNetGame && useDialogs()) {
		quickLoadFile();
	}
}
void HK_exitGame(int)
{
	audioInterface.blank();
	if (vidFullscreen) {
		vidFullscreen = 0;
		videoInterface.exit();
	}
	if (driverOkay) {
		stopReplay();
		aviStop();
		BurnerDrvExit();
		if (kNetGame) {
			FBAKailleraEnd();
			PostQuitMessage(0);
		}
		cheatsAllowed = true; // reenable cheats netplay has ended

		mediaReInit();
		menuSync(MENUT_ALL);
	}
}

// key handle
static inline bool MHkeysCheckMenuState(const CustomKey* key)
{
	if (!key || (key->menuid > 0 && GetMenuState(hMainMenu, key->menuid, MF_BYCOMMAND) == MF_GRAYED)) {
		return false;
	}
	return true;
}

int MHkeysDownHandle(const MSG& msg)
{
	static int key = 0;
	static int modifiers = 0;
	static int processed = 0;

	key = msg.wParam;
	modifiers = 0;
	if (KEY_DOWN(VK_CONTROL)) {
		modifiers |= MODKEY_CTRL;
	}
	if (KEY_DOWN(VK_MENU)) {
		modifiers |= MODKEY_ALT;
	}
	if (KEY_DOWN(VK_SHIFT)) {
		modifiers |= MODKEY_SHIFT;
	}

	processed = 0;

	CustomKey* customkey = &customKeys[0];
	while (!lastCustomKey(*customkey)) {
		if (key == customkey->key && modifiers == customkey->keymod && customkey->handleKeyDown) {
			if (MHkeysCheckMenuState(customkey)) {
				customkey->handleKeyDown(customkey->param);
				processed = 1;
			}
		}
		customkey++;
	}

	return processed;
}

int MHkeysUpHandle(const MSG& msg)
{
	static int key = 0;
	static int modifiers = 0;
	static int processed = 0;

	key = msg.wParam;
	modifiers = 0;
	if (KEY_DOWN(VK_CONTROL)) {
		modifiers |= MODKEY_CTRL;
	}
	if (KEY_DOWN(VK_MENU)) {
		modifiers |= MODKEY_ALT;
	}
	if (KEY_DOWN(VK_SHIFT)) {
		modifiers |= MODKEY_SHIFT;
	}

	processed = 0;

	CustomKey* customkey = &customKeys[0];
	while (!lastCustomKey(*customkey)) {
		if (customkey->handleKeyUp && key == customkey->key && modifiers == customkey->keymod) {
			customkey->handleKeyUp(customkey->param);
			processed = 1;
		}
		customkey++;
	}

	return processed;
}
